import type {AxiosInstance, AxiosResponse} from 'axios';
import axios from 'axios';
import type {RequestClientConfig, RequestClientOptions} from '../types';

import {bindMethods, merge} from '#/utils';

import {InterceptorManager} from '../interceptor/manager';
import {FileUploader} from '../modules/uploader';
import {FileDownloader} from '../modules/downloader';

import {getParamsSerializer} from '../serializer';

class RequestClient {
    public addRequestInterceptor: InterceptorManager['addRequestInterceptor'];

    public addResponseInterceptor: InterceptorManager['addResponseInterceptor'];
    public download: FileDownloader['download'];

    // 是否正在刷新token
    public isRefreshing = false;
    // 刷新token队列
    public refreshTokenQueue: ((token: string) => void)[] = [];
    public upload: FileUploader['upload'];
    private readonly instance: AxiosInstance;

    /**
     * 构造函数，用于创建Axios实例
     * @param options - Axios请求配置，可选
     */
    constructor(options: RequestClientOptions = {}) {
        // 合并默认配置和传入的配置
        const defaultConfig: RequestClientOptions = {
            headers: {
                'Content-Type': 'application/json;charset=utf-8',
            },
            responseReturn: 'raw',
            // 默认超时时间
            timeout: 10_000,
        };
        const {...axiosConfig} = options;
        const requestConfig = merge(axiosConfig, defaultConfig);
        requestConfig.paramsSerializer = getParamsSerializer(
            requestConfig.paramsSerializer,
        );
        this.instance = axios.create(requestConfig);

        bindMethods(this);

        // 实例化拦截器管理器
        const interceptorManager = new InterceptorManager(this.instance);
        this.addRequestInterceptor =
            interceptorManager.addRequestInterceptor.bind(interceptorManager);
        this.addResponseInterceptor =
            interceptorManager.addResponseInterceptor.bind(interceptorManager);

        // 实例化文件上传器
        const fileUploader = new FileUploader(this);
        this.upload = fileUploader.upload.bind(fileUploader);
        // 实例化文件下载器
        const fileDownloader = new FileDownloader(this);
        this.download = fileDownloader.download.bind(fileDownloader);
    }

    /**
     * DELETE请求方法
     */
    public delete<T = any>(url: string, config?: RequestClientConfig,): Promise<T> {
        return this.request<T>(url, {...config, method: 'DELETE'});
    }

    /**
     * GET请求方法
     */
    public get<T = any>(url: string, config?: RequestClientConfig): Promise<T> {
        return this.request<T>(url, {...config, method: 'GET'});
    }

    /**
     * POST请求方法
     */
    public post<T = any>(url: string, data?: any, config?: RequestClientConfig,): Promise<T> {
        return this.request<T>(url, {...config, data, method: 'POST'});
    }

    /**
     * PUT请求方法
     */
    public put<T = any>(url: string, data?: any, config?: RequestClientConfig,): Promise<T> {
        return this.request<T>(url, {...config, data, method: 'PUT'});
    }

    /**
     * 通用的请求方法
     */
    public async request<T>(url: string, config: RequestClientConfig,): Promise<T> {
        try {
            const response: AxiosResponse<T> = await this.instance({
                url,
                ...config,
                ...(config.paramsSerializer
                    ? {paramsSerializer: getParamsSerializer(config.paramsSerializer)}
                    : {}),
            });
            return response as T;
        } catch (error: any) {
            throw error.response ? error.response.data : error;
        }
    }
}

export {RequestClient};
